#ifndef EXPRESSION_VALIDATOR_H
#define EXPRESSION_VALIDATOR_H

#include "../expressions/expressioncommon.h"
#include "../types/wfnettypes.h"

#include <coretypes.h>

#include <QtCore/QHash>
#include <QtCore/QList>
#include <QtCore/QPair>
#include <QtCore/QSet>
#include <QtCore/QStack>
#include <QtCore/QVariant>

using namespace core;
using namespace net;

namespace core
{
class Type;
}

namespace wfnet
{
class WfNet;

class ExpressionValidator
{
    class ParseStep;

    typedef QString Token;
    typedef QString TokenMask;
    typedef QString OperationMethod;
    typedef QList<QPair<Token, uint> > TokenList;   

public:
    static ExpressionValidator* getInstance();
    static void destroy();

    bool validate(const Expr& expr, const TypeName& context, bool timed,
        WfNet* net);

    UserVariables getFoundVariables() const;
    SystemVariables getSystemVariables() const;
    Expr getSystemExpression() const;
    TypeName getReturnType() const;
    uint getExprType() const;

    void registerKeywordMasks(const QSet<ValueKeywordMask>& masks);
    void clearKeywordMasks();

private:
    ExpressionValidator();
    ~ExpressionValidator();

    bool parseTokens(const Expr& expr, ParseStep* step);
    bool parseOperations(ParseStep* step, const TypeName& context,
        bool timed, WfNet* net);

    bool unfoldExpressions(ParseStep* currentStep,
         QSet<ParseStep* >& nextLevel);
    bool checkVariables(ParseStep* step, bool timed, WfNet* net) const;

    const Type* determineValueType(const TypeValue& value,
        const TypeName& context, bool timed,
        WfNet* net);
    const Type* determineVariableType(const VariableName& variable,
        const TypeName& context, bool timed, WfNet* net);
    const Type* convertToColoSet(const TypeName& name,
        const TypeName& context, bool timed, WfNet* net);

    void saveSubExprResult(ParseStep* step);

    void makeSystemExpression(const Expr& expr);
    void changeValuesToSystemVariables(const Expr& expr,
        const ValueKeywordMask& mask);

    void makeMultisetKeywords();

    void clean();

private:
    static const TokenMask MT_VARIABLE_MASK;
    static const TokenMask VARIABLE_MASK;
    static const TokenMask OPERATOR_MASK;
    static const TokenMask EXPRESSION_MASK;

    char m_systemVariableChar;

    Expr m_systemExpression;
    uint m_exprType;
    TypeName m_expressionReturnType;

    QStack<ParseStep* > m_steps;
    QHash<Expr, QPair<TypeName, Expr> > m_exprMapping;
    QSet<ValueKeywordMask> m_keywordMasks;
    QSet<ValueKeywordMask> m_multisetKeywordMasks;

    QSet<OperationName> m_boolOperations;
    QSet<OperationName> m_typeOperations;
    QHash<OperationName, OperationMethod> m_operationToMethodMapping;

    UserVariables m_lastFoundVariables;
    SystemVariables m_lastSystemVariables;

    QHash<TypeValue, QPair<TypeName, VariableName> > m_determinedValueTypes;
};

} // namespace wfnet

#endif
